{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0",
   "metadata": {},
   "source": [
    "# Best practices\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1",
   "metadata": {},
   "source": [
    "## Use cell decorator\n",
    "\n",
    "- Do not name leave cells unnamed.\n",
    "\n",
    "\n",
    "Unnamed cells are going to get different names every time you run and it is going to be hard to know where they come from."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2",
   "metadata": {},
   "outputs": [],
   "source": [
    "import gdsfactory as gf\n",
    "\n",
    "c = gf.Component()\n",
    "print(c.name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3",
   "metadata": {},
   "source": [
    "- Do not name cells manually. Manually defining names can create duplicated cells."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "c1 = gf.Component(\"my_cell1\")\n",
    "c2 = gf.Component(\"my_cell2\")\n",
    "c1.add_ref(gf.components.straight(length=10))\n",
    "c2.add_ref(gf.components.straight(length=11))\n",
    "\n",
    "c3 = gf.Component(\"im_going_to_have_duplicated_cell_names\")\n",
    "_ = c3.add_ref(c1)\n",
    "_ = c3.add_ref(c2)\n",
    "c3.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "source": [
    "Solution: Use the cell decorator to name cells."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "@gf.cell\n",
    "def my_pcell(length=10):\n",
    "    c = gf.Component()\n",
    "    ref = c.add_ref(gf.components.straight(length=length))\n",
    "    c.add_ports(ref.ports)\n",
    "    return c\n",
    "\n",
    "\n",
    "print(my_pcell(length=11).name)\n",
    "print(my_pcell(length=12).name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "source": [
    "## Keep cell functions simple\n",
    "\n",
    "As you make functions made of other functions one can start passing a lot of arguments to the function. This makes the code hard to write, read and maintain.\n",
    "\n",
    "- Avoid complicated functions with many parameters.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8",
   "metadata": {},
   "outputs": [],
   "source": [
    "@gf.cell\n",
    "def mzi_with_bend_overly_complicated(\n",
    "    mzi_delta_length: float = 10.0,\n",
    "    mzi_length_y: float = 2.0,\n",
    "    mzi_length_x: float | None = 0.1,\n",
    "    bend_radius: float = 10,\n",
    "    bend_cross_section=\"strip\",\n",
    "):\n",
    "    \"\"\"Returns MZI interferometer with bend.\"\"\"\n",
    "    c = gf.Component()\n",
    "    mzi1 = c.add_ref(\n",
    "        gf.components.mzi(\n",
    "            delta_length=mzi_delta_length,\n",
    "            length_y=mzi_length_y,\n",
    "            length_x=mzi_length_x,\n",
    "        )\n",
    "    )\n",
    "    bend1 = c.add_ref(\n",
    "        gf.components.bend_euler(radius=bend_radius, cross_section=bend_cross_section)\n",
    "    )\n",
    "    bend1.connect(\"o1\", mzi1.ports[\"o2\"])\n",
    "    c.add_port(\"o1\", port=mzi1.ports[\"o1\"])\n",
    "    c.add_port(\"o2\", port=bend1.ports[\"o2\"])\n",
    "    return c\n",
    "\n",
    "\n",
    "c = mzi_with_bend_overly_complicated(bend_radius=100)\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9",
   "metadata": {},
   "source": [
    "Solution:\n",
    "\n",
    "- leverage `functools.partial` to customize the default parameters of a function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "10",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "from functools import partial\n",
    "\n",
    "\n",
    "@gf.cell\n",
    "def mzi_with_bend(mzi=gf.components.mzi, bend=gf.components.bend_euler):\n",
    "    \"\"\"Returns MZI interferometer with bend.\"\"\"\n",
    "    c = gf.Component()\n",
    "    mzi1 = c.add_ref(mzi())\n",
    "    bend1 = c.add_ref(bend())\n",
    "    bend1.connect(\"o1\", mzi1.ports[\"o2\"])\n",
    "    c.add_port(\"o1\", port=mzi1.ports[\"o1\"])\n",
    "    c.add_port(\"o2\", port=bend1.ports[\"o2\"])\n",
    "    return c\n",
    "\n",
    "# The partial function from Python's functools library is used to create a new function called bend_big.\n",
    "# This new function is a version of the standard bend_euler component, but with the radius parameter permanently set to 100.\n",
    "bend_big = partial(gf.components.bend_euler, radius=100)\n",
    "\n",
    "# This argument overrides the default bend component used in the MZI and replaces it with the custom, larger-radius bend factory defined in the previous step.\n",
    "c = mzi_with_bend(bend=bend_big)\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "source": [
    "## Use array of references\n",
    "\n",
    "- Array of references are more memory efficient and faster to create."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "@gf.cell\n",
    "def pad_array_slow(\n",
    "    cols: int = 30,\n",
    "    rows: int = 30,\n",
    "    spacing: tuple[float, float] = (200, 200),\n",
    "    pad=gf.components.pad,\n",
    "):\n",
    "    \"\"\"Returns a grid of pads. BAD SLOW CODE. Please don't use this. See pad_array_fast instead.\"\"\"\n",
    "    xspacing, yspacing = spacing\n",
    "    c = gf.Component()\n",
    "    for col in range(cols):\n",
    "        for row in range(rows):\n",
    "            c.add_ref(pad()).movex(col * xspacing).movey(row * yspacing)\n",
    "    return c\n",
    "\n",
    "\n",
    "c = pad_array_slow()\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13",
   "metadata": {},
   "outputs": [],
   "source": [
    "@gf.cell\n",
    "def pad_array_fast(\n",
    "    cols: int = 30,\n",
    "    rows: int = 30,\n",
    "    column_pitch: float = 200,\n",
    "    row_pitch: float = 200,\n",
    "    pad=gf.components.pad,\n",
    ") -> gf.Component:\n",
    "    \"\"\"Returns a grid of pads. GOOD CODE. USE THIS.\n",
    "\n",
    "    Args:\n",
    "        cols: number of columns.\n",
    "        rows: number of rows.\n",
    "        column_pitch: distance between columns.\n",
    "        row_pitch: distance between rows.\n",
    "        pad: pad cell.\n",
    "    \"\"\"\n",
    "    c = gf.Component()\n",
    "    c.add_ref(\n",
    "        pad(), columns=cols, rows=rows, column_pitch=column_pitch, row_pitch=row_pitch\n",
    "    )\n",
    "    return c\n",
    "\n",
    "\n",
    "c = pad_array_fast()\n",
    "c.plot()"
   ]
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "-all",
   "custom_cell_magics": "kql"
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
